// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module lvds_channel_ctrl_host
#(
parameter           CONSECUTIVE_K28_5_LOCK        =   5,               //Define how many K28.5 comma should be consecutive detected
parameter           CONSECUTIVE_K28_1_LOCK        =   5,               //Define how many K28.1 comma should be consecutive detected
parameter           CONSECUTIVE_K28_1_Loss        =   3,               //Define how many K28.1 comma are consecutive detected loss
parameter           CONSECUTIVE_CRC_Loss          =   10               //Define how many CRC check are consecutive detected wrong
)
(
input               clock,
input               reset_n,



input        [9:0]  lvds_rx_data,
output  reg  [9:0]  lvds_tx_data,

input               bist_mode_enable,           //from csr, only valid on host side
input        [1:0]  inject_frame_comma_err,
input        [1:0]  inject_frame_crc_err,
input        [3:0]  frame_length,
input               fifo_rdempty,

input        [7:0]  data_tx0,                  //Data_tx comes from each IO/Memory based channel
input        [7:0]  data_tx1,
input        [7:0]  data_tx2,
input        [7:0]  data_tx3,
input        [7:0]  data_tx4,
input        [7:0]  data_tx5,
input        [7:0]  data_tx6,
input        [7:0]  data_tx7,                  //UART0_1
input        [7:0]  data_tx8,
output  reg  [7:0]  data_rx0,                  //Data_rx is drived to each IO/Memory based channel
output  reg  [7:0]  data_rx1,
output  reg  [7:0]  data_rx2,
output  reg  [7:0]  data_rx3,
output  reg  [7:0]  data_rx4,
output  reg  [7:0]  data_rx5,
output  reg  [7:0]  data_rx6,
output  reg  [7:0]  data_rx7,
output  reg  [7:0]  data_rx8,
output              bit_slip,
output  reg  [4:0]  frame_count,               //frame_data[4:0]
output  reg  [11:0] tx_frm_offset,             //Mark the byte offset within a tx frame.
output  reg  [11:0] rx_frm_offset,             //Mark the byte offset within a rx frame.
output       [4:0]  frame_count_check,         //frame_data_check[4:0]
output              gpio_i_sample_en,
output  reg         gpio_o_sample_en,
output              aligned,
output              frame_crc_err,
output  reg         link_training_err,
output              frame_comma_err,
output              frame_loss_crc_H2A,        //Host receives frame_loss_crc indication from agent that H2A direction has error, id_bit6
output  reg         frame_aligned_tx,          //indicate H2A aligned at host side and A2H aligned at agent side
output  reg         frame_loss_crc_A2H,        //Host detects frame_loss_crc indication from agent by Rx detection that A2H direction has error
output              bist_err
);


//////////////////////////////////////////////////////////////////////////////////
// States for FSM
//////////////////////////////////////////////////////////////////////////////////
localparam    ST_INIT                         = 4'd0;
localparam    ST_HOST_LT_COMMA_HUNTING        = 4'd1;
localparam    ST_WAIT_A2H_ALIGNED             = 4'd2;
localparam    ST_HOST_ALIGNED                 = 4'd3;
localparam    ST_A2H_FRAME_LOSS               = 4'd4;
localparam    ST_LT_ERR                       = 4'd5;
	
	
//////////////////////////////////////////////////////////////////////////////////
// Parameters
//////////////////////////////////////////////////////////////////////////////////
localparam    LOW                             = 1'b0;
localparam    HIGH                            = 1'b1;
localparam    TRAIN_COMMA_8BIT                = 8'hBC;              //Comma K28.5 for initial link training
localparam    TRAIN_COMMA_P                   = 10'b10_1000_0011;
localparam    TRAIN_COMMA_N                   = 10'b01_0111_1100;
localparam    FRAME_COMMA_8BIT                = 8'h3C;              //Comma K28.1 for lvds frame alignment
localparam    FRAME_COMMA_P                   = 10'b01_1000_0011;
localparam    FRAME_COMMA_N                   = 10'b10_0111_1100;
	
`ifdef SIMULATION
localparam    T_10mS_20M                      = 20'd200000;

`else 
localparam    T_10mS_20M                      = 20'd200000;

`endif 

//////////////////////////////////////////////////////////////////////////////////
// Internal Signals
//////////////////////////////////////////////////////////////////////////////////
reg	[3:0]	  rstate;	
wire  [9:0]   lvds_tx_data_postenc; 
wire          enc_rd;                                   //8b10b encoding RD
wire          dec_rd;                                   //8b10b decoding RD
wire  [7:0]   crc_data;
wire  [7:0]   crc_check_result;

reg   [7:0]   frame_tx_data;                            //Used to construct lvds frame before 8b/10b
wire  [7:0]   frame_rx_data;                            //Used to recover data for each IO/Memory channel
reg   [9:0]   lvds_rx_data_d;
reg   [9:0]   lvds_rx_data_d2;
reg           lvds_rx_data_d_not_frame_com;             //indicate rx data delay is not frame comma
reg           crc_gen_en;
reg           crc_check_en;
reg           gpio_aligned;
reg           gpio_aligned_d;    
reg           gpio_data_not_valid;

wire          bist_en_tx_host;                             //frame_id_tx at host side bit7
reg           frame_loss_tx;                            //indicate A2H frame loss for host and H2A frame loss for agent
wire  [7:0]   frame_id_tx;

wire          bist_en_rx_host;
wire          frame_loss_rx;                            //indicate H2A frame loss for host and A2H frame loss for agent
wire          frame_aligned_rx;                         //indicate A2H aligned at host side and H2A aligned at agent side
reg   [7:0]   frame_id_rx;    

reg           timer_10ms_start;
wire          timer_10ms_done;

reg           train_comma_sm_en;
wire          train_comma_locked;

reg           frame_comma_sm_en;
wire          frame_comma_locked;

reg           rx_frame_bdry_found;

reg           frame_loss_sm_en;
wire          frame_comma_mismatch;
wire          frame_re_aligned;
wire          frame_lost;

reg           ext_trigger_d;
reg           ext_trigger_dd;

reg           ext_trigger_edge;

reg           ext_trigger_flag;

reg  [3:0]    trig_cnt;

reg  [3:0]    init_delay_cnt;

wire          crc_check_clr;

reg           prbs_gen_en;
wire [7:0]    prbs_gen_data;
reg           prbs_check_en;
reg  [7:0]    prbs_check_data;
wire [7:0]    prbs_check_result;

//Encoder K character enable logic
wire          enc_kin_ena;
//Encoder data enable logic
wire          enc_ein_ena;
   
//error injection
wire          inject_frame_comma_err_ind;
wire          inject_1_frame_comma_err;
wire          inject_3_frame_comma_err;
reg           inject_frame_comma_err_d;
reg           inject_frame_comma_err_flag;
reg  [3:0]    inject_frame_comma_err_cnt;


wire          inject_frame_crc_err_ind;
wire          inject_1_frame_crc_err;
wire          inject_10_frame_crc_err;
reg           inject_frame_crc_err_d;
reg           inject_frame_crc_err_flag;
reg  [3:0]    inject_frame_crc_err_cnt;

//////////////////////////////////////////////////////////////////////////////////
// Combiantional Logic
//////////////////////////////////////////////////////////////////////////////////
assign        gpio_i_sample_en        =    frame_aligned_tx && (tx_frm_offset == 2);

assign        frame_id_tx             =    {bist_en_tx_host, frame_loss_tx, frame_aligned_tx, frame_count};

assign        bist_en_rx_host         =    frame_id_rx[7];
assign        frame_loss_rx           =    frame_id_rx[6];
assign        frame_aligned_rx        =    frame_id_rx[5];
assign        frame_count_check       =    frame_id_rx[4:0];

assign        bist_en_tx_host         =    bist_mode_enable;
														 
assign        frame_comma_mismatch    =    frame_aligned_tx && (rx_frm_offset == 4'd1 && (lvds_rx_data_d2 != FRAME_COMMA_P && lvds_rx_data_d2 != FRAME_COMMA_N));
assign        frame_comma_err         =    frame_comma_mismatch;

assign        frame_crc_err           =    rx_frm_offset == 4'd1 && crc_check_result != 8'h0;

assign        aligned                 =    frame_aligned_rx;

assign        crc_check_clr           =    (rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING|| rstate == ST_LT_ERR) || (rx_frame_bdry_found && rx_frm_offset == 4'd1);

assign        bist_err                =    |prbs_check_result;

assign        frame_loss_crc_H2A      =    frame_loss_rx;

assign        enc_kin_ena             =    (rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING || rstate == ST_LT_ERR) ? 1'b1 : ((tx_frm_offset == 4'd0) ? 1'b1 : 1'b0);
assign        enc_ein_ena             =    !enc_kin_ena;

//error injection
assign        inject_frame_comma_err_ind  =   inject_frame_comma_err[0] ^ inject_frame_comma_err[1];
assign        inject_1_frame_comma_err    =   inject_frame_comma_err[0] && !inject_frame_comma_err[1];
assign        inject_3_frame_comma_err    =   !inject_frame_comma_err[0] && inject_frame_comma_err[1];

assign        inject_frame_crc_err_ind    =   inject_frame_crc_err[0] ^ inject_frame_crc_err[1];
assign        inject_1_frame_crc_err      =   inject_frame_crc_err[0] && !inject_frame_crc_err[1];
assign        inject_10_frame_crc_err     =   !inject_frame_crc_err[0] && inject_frame_crc_err[1];


always@(posedge clock or negedge reset_n) begin
    if(!reset_n) begin
        rx_frame_bdry_found <=  0;
    end else begin
        rx_frame_bdry_found <=    (rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING || rstate == ST_LT_ERR) ? LOW : 
                                           (((lvds_rx_data_d != FRAME_COMMA_P && lvds_rx_data_d != FRAME_COMMA_N) && (lvds_rx_data == FRAME_COMMA_P || lvds_rx_data == FRAME_COMMA_N)) ? HIGH : rx_frame_bdry_found);  //set high when first time caputure K28.1
	   end
end			

//Delay for ST_INIT
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   init_delay_cnt                  <=   4'b0;
	end
	else begin
	   if(rstate == ST_INIT) begin
		   init_delay_cnt               <=   init_delay_cnt + 1'b1;
		end
		else begin
		   init_delay_cnt               <=   4'b0;
		end
	end
end


always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
		lvds_tx_data                    <=   0;
		train_comma_sm_en               <=   LOW;
		frame_comma_sm_en               <=   LOW;
		timer_10ms_start                <=   LOW;
		frame_loss_tx                   <=   LOW;
		frame_aligned_tx                <=   LOW;
		frame_loss_sm_en                <=   LOW;
		link_training_err               <=   LOW;
		
		rstate                          <=   ST_INIT;
	end
	
	else begin
	   case(rstate)
		   ST_INIT: begin
			   lvds_tx_data              <=   lvds_tx_data_postenc;
				train_comma_sm_en         <=   LOW;
				frame_comma_sm_en         <=   LOW;
		      timer_10ms_start          <=   LOW;
				frame_loss_tx             <=   LOW;
		      frame_aligned_tx          <=   LOW;
				frame_loss_sm_en          <=   LOW;
				link_training_err         <=   LOW;
				
				if(init_delay_cnt == 4'd10) begin
				   rstate                 <=   ST_HOST_LT_COMMA_HUNTING;
				end
			end //end case of ST_INIT
			
			ST_HOST_LT_COMMA_HUNTING: begin
			   lvds_tx_data              <=   lvds_tx_data_postenc;
				timer_10ms_start          <=   HIGH;            
				train_comma_sm_en         <=   HIGH;
				frame_comma_sm_en         <=   LOW;
				frame_loss_tx             <=   LOW;
		      frame_aligned_tx          <=   LOW;
				frame_loss_sm_en          <=   LOW;
				link_training_err         <=   LOW;
				
				if(timer_10ms_done) begin
				   rstate                 <=   ST_LT_ERR;                 //10ms timeout
				end
				else if(train_comma_locked) begin                         //CONSECUTIVE_K28_5_LOCK times consecutive K28.5 locked
				   rstate                 <=   ST_WAIT_A2H_ALIGNED;
				end
			end //end case of ST_HOST_LT_COMMA_HUNTING
			
			ST_WAIT_A2H_ALIGNED: begin
				lvds_tx_data              <=   lvds_tx_data_postenc;      
				timer_10ms_start          <=   HIGH;            
				train_comma_sm_en         <=   LOW;             //Otherwise train_comma_locked will be always high
				frame_comma_sm_en         <=   HIGH;
				frame_loss_tx             <=   LOW;
		      frame_aligned_tx          <=   LOW;
				frame_loss_sm_en          <=   LOW;
				link_training_err         <=   LOW;
				
				if(timer_10ms_done) begin
				   rstate                 <=   ST_LT_ERR;                 //10ms timeout
				end
				else if(!rx_frame_bdry_found && lvds_rx_data_d_not_frame_com) begin
				   rstate                 <=   ST_HOST_LT_COMMA_HUNTING;
				end
				else if(frame_comma_locked) begin
				   rstate                 <=   ST_HOST_ALIGNED;
				end
            
			end //end case of ST_WAIT_A2H_ALIGNED
			
			ST_HOST_ALIGNED: begin
			   lvds_tx_data              <=   lvds_tx_data_postenc;
				timer_10ms_start          <=   LOW;
				train_comma_sm_en         <=   LOW;                       //Otherwise train_comma_locked will be always high
				frame_comma_sm_en         <=   LOW;                       //Otherwise frame_comma_locked will be always high
				frame_loss_tx             <=   LOW;
		      frame_aligned_tx          <=   HIGH;
				frame_loss_sm_en          <=   LOW;
				link_training_err         <=   LOW;

            if(frame_comma_mismatch || frame_crc_err) begin
   				rstate                 <=   ST_A2H_FRAME_LOSS;
   		   end

			end //end case of ST_HOST_ALIGNED
			
			ST_A2H_FRAME_LOSS: begin
			   lvds_tx_data              <=   lvds_tx_data_postenc;
				timer_10ms_start          <=   LOW;
				train_comma_sm_en         <=   LOW;                       //Otherwise train_comma_locked will be always high
				frame_comma_sm_en         <=   LOW;                       //Otherwise frame_comma_locked will be always high
				frame_loss_tx             <=   HIGH;                     //Indicate that A2H direction link error
		      frame_aligned_tx          <=   HIGH;
				frame_loss_sm_en          <=   HIGH;
				link_training_err         <=   LOW;
				
				if(frame_lost) begin
				   rstate                 <=   ST_INIT;
				end
				else if(frame_re_aligned) begin
				   rstate                 <=   ST_HOST_ALIGNED;
				end
			end //end case of ST_A2H_FRAME_LOSS
			
			ST_LT_ERR: begin
			   lvds_tx_data              <=   lvds_tx_data_postenc;
				train_comma_sm_en         <=   LOW;
				frame_comma_sm_en         <=   LOW;
		      timer_10ms_start          <=   LOW;                       //timer cleared
				frame_loss_tx             <=   LOW;
		      frame_aligned_tx          <=   LOW;
				frame_loss_sm_en          <=   LOW;
				link_training_err         <=   HIGH;                      //log error
				
				rstate                    <=   ST_INIT;
			end //end case of ST_LT_ERR
			
			default: begin
			   rstate                    <=   ST_INIT;
			end //end case of default
		endcase
	end

end
	

//tx_frm_offset on tx side, use to determine frame num in one payload package of each data to be put on link
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   tx_frm_offset                   <=   4'd0;
	end
	
	else begin
	   if(rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING|| rstate == ST_LT_ERR) begin
		   tx_frm_offset                <=   4'd0;
		end
		else begin
		   if(tx_frm_offset == frame_length) begin
			   tx_frm_offset             <=   4'd0;
			end
			else begin
			   tx_frm_offset             <=   tx_frm_offset + 1'b1;
			end
		end
	end
end 

//frame_tx_data on tx side, use to determine which frame data to be put on link before encoding
//using combinational logic
always @ (*) begin
   if(rstate == ST_INIT || rstate == ST_LT_ERR) begin
	   frame_tx_data                    =   8'b0;
	end
	else if (rstate == ST_HOST_LT_COMMA_HUNTING) begin
	   frame_tx_data                    =   TRAIN_COMMA_8BIT;
	end
	else begin
	case(tx_frm_offset)
		4'd0: begin
			   if(rstate  == ST_HOST_ALIGNED && inject_frame_comma_err_flag) begin
				   frame_tx_data           =   8'h3D;
				end
				else begin
				   frame_tx_data           =   FRAME_COMMA_8BIT;
				end
		end //end case of tx_frm_offset=0
		
		4'd1: begin
			frame_tx_data                 =   frame_id_tx;
		end //end case of tx_frm_offset=1
		
		4'd2: begin
			if(frame_aligned_tx) begin
			   if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx0;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=2
		
		4'd3: begin
			if(frame_aligned_tx) begin
			   if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx1;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=3
		
		4'd4: begin
			if(frame_aligned_tx) begin
			   if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx2;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=4
		
		4'd5: begin
			if(frame_aligned_tx) begin
			   if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx3;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=5
		
		4'd6: begin
			if(frame_aligned_tx) begin
			   if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx4;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=6
		
		4'd7: begin
			if(frame_aligned_tx) begin
				if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx5;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=7
		
		4'd8: begin
			if(frame_aligned_tx) begin
				if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx6;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=8
		
		4'd9: begin
			if(frame_aligned_tx) begin
				if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx7;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=9
		
		4'd10: begin
			if(frame_aligned_tx) begin
				if(bist_mode_enable) begin
				   frame_tx_data           =   prbs_gen_data;
				end
				else begin
				   frame_tx_data           =   data_tx8;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end
		end //end case of tx_frm_offset=10
		
		4'd11: begin
			if(frame_aligned_tx) begin
			   if(inject_frame_crc_err_flag) begin
				   frame_tx_data              =   {crc_data[7], crc_data[6], crc_data[5], crc_data[4], crc_data[3], crc_data[2], crc_data[1], ~crc_data[0]};
				end
				else begin
				   frame_tx_data              =   crc_data;
				end
			end
			else begin
				frame_tx_data              =   8'hFF;
			end   
		end //end case of tx_frm_offset=11
		
		default: begin
			frame_tx_data                 =   8'hFF;
		end //end case of default
	endcase
	end
end


//frame_count on tx side to determine which GPIO group to be put on link
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   frame_count                     <=   5'd0;
	end
	   
	else begin
	   if(rstate == ST_INIT || rstate == ST_LT_ERR || rstate == ST_HOST_LT_COMMA_HUNTING || rstate == ST_WAIT_A2H_ALIGNED) begin
		   frame_count                  <=   5'd0;
		end
		else begin
		   if(tx_frm_offset == frame_length) begin
			   frame_count               <=   frame_count + 1'b1;
			end
		end
	end
end

//delay lvds_rx_data for 1 clock
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   lvds_rx_data_d                  <=   10'd0;
       lvds_rx_data_d_not_frame_com    <=   1'b1;
	end
	else begin
	   lvds_rx_data_d                  <=   lvds_rx_data;
	   lvds_rx_data_d2                 <=   lvds_rx_data_d;
	   lvds_rx_data_d_not_frame_com    <=   (lvds_rx_data_d != TRAIN_COMMA_P && lvds_rx_data_d != TRAIN_COMMA_N);
	end
end	

//rx_frm_offset on rx side, use to determine frame num in one payload package of each data received from link
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   rx_frm_offset                   <=   4'd0;
	end
	
	else begin
	   if(rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING|| rstate == ST_LT_ERR) begin
		   rx_frm_offset                <=   4'd0;
		end
		else begin
		   if(rx_frame_bdry_found) begin
		      if(rx_frm_offset == frame_length) begin
			      rx_frm_offset          <=   4'd0;
			   end
			   else begin
				   if(!fifo_rdempty) begin
			         rx_frm_offset       <=   rx_frm_offset + 1'b1;
					end
			   end
			end
		end
	end
end 

//recover frame data on rx side based on rx_frm_offset
//payload need to be after of one clock to match frame strcuture
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   frame_id_rx                     <=   8'h0;
		data_rx0                        <=   8'h0;
		data_rx1                        <=   8'h0;
		data_rx2                        <=   8'h0;
		data_rx3                        <=   8'h0;
		data_rx4                        <=   8'h0;
		data_rx5                        <=   8'h0;
		data_rx6                        <=   8'h0;
		data_rx7                        <=   8'h77;      //after aligned before first data_rx7 received, default value shuold be 4'h7 for each uart channel
		data_rx8                        <=   8'hFF;
		prbs_check_data                 <=   8'hFF;
	end
	else begin
	   //handle frame_id_rx data independently
	   if(rstate == ST_INIT || rstate == ST_HOST_LT_COMMA_HUNTING|| rstate == ST_LT_ERR) begin
		   frame_id_rx                  <=   8'h0;
		end
		else begin 
		   if(rx_frame_bdry_found && rx_frm_offset == 4'd2) begin
		      frame_id_rx               <=   frame_rx_data;
			end
		end
	   //handle io payload data
	   if(!frame_aligned_rx) begin
		   data_rx0                     <=   8'h0;
		   data_rx1                     <=   8'h0;
		   data_rx2                     <=   8'h0;
		   data_rx3                     <=   8'h0;
		   data_rx4                     <=   8'h0;
		   data_rx5                     <=   8'h0;
		   data_rx6                     <=   8'h0;
		   data_rx7                     <=   8'h77;      
		   data_rx8                     <=   8'hFF;
			prbs_check_data              <=   8'hFF;
		end
		else begin
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd3) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;
				end
				else begin
				   data_rx0               <=   frame_rx_data;
				end
			end
			else begin
			   data_rx0                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd4) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx1               <=   frame_rx_data;
				end
			end   
			else begin
			   data_rx1                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd5) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx2               <=   frame_rx_data;
				end
			end
			else begin
			   data_rx2                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd6) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx3               <=   frame_rx_data;
				end
			end
			else begin
			   data_rx3                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd7) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx4               <=   frame_rx_data;
				end
			end
			else begin
			   data_rx4                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd8) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx5               <=   frame_rx_data;
				end
			end
			else begin
			   data_rx5                  <=   8'h0;
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd9) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx6               <=   frame_rx_data;
				end
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd10) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx7               <=   frame_rx_data;
				end
			end
			
			if(rstate == ST_HOST_ALIGNED && rx_frm_offset == 4'd11) begin
			   if(bist_en_rx_host) begin
				   prbs_check_data        <=   frame_rx_data;     
				end
				else begin
				   data_rx8               <=   frame_rx_data;
				end
			end
		end
	end
end


//Handle CRC related data
//using combinational logic
always @ (*) begin
   if(!reset_n) begin
	   crc_gen_en                       =   LOW;
	end
	else begin
	   if(frame_aligned_tx) begin
		   crc_gen_en                    =   HIGH;
		end
		else begin
		   crc_gen_en                    =   LOW;
		end
	end
end

//using combinational logic
always @ (*) begin
   if(!reset_n) begin
	   crc_check_en                     =   LOW;
	end
	else begin
	   if((rx_frm_offset == 4'd2 && frame_rx_data[5]) || frame_aligned_rx) begin
		   crc_check_en                  =   HIGH;
		end
		else begin
		   crc_check_en                  =   LOW;
		end
	end
end


//GPIO handling, if CRC error happens on one transaction, then keep GPIO as last known good signals
always @ (posedge clock or negedge reset_n) begin
    if(!reset_n) begin
        gpio_o_sample_en              <=   LOW;
    end
    else begin
        if(rstate == ST_HOST_ALIGNED && frame_aligned_rx && rx_frm_offset == 4'd1 && !frame_crc_err) begin
            gpio_o_sample_en          <=   HIGH;
        end
        else begin
            gpio_o_sample_en          <=   LOW;
        end
    end
end

//LFSR Gen enable for host
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   prbs_gen_en                     <=   LOW;
	end
	else begin
	   if(frame_aligned_tx && bist_mode_enable) begin
		   if(tx_frm_offset > 8'h0 && tx_frm_offset < 8'ha) begin
		      prbs_gen_en               <=   HIGH;
			end
			else begin
			   prbs_gen_en               <=   LOW;
			end
		end
		else begin
		   prbs_gen_en                  <=   LOW;
		end
	end
end

//LFSR Check enable for host
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   prbs_check_en                   <=   LOW;
	end
	else begin
	   if(frame_aligned_rx && bist_en_rx_host) begin
		   if(rx_frm_offset > 8'h2 && rx_frm_offset <= 8'hb) begin
		      prbs_check_en             <=   HIGH;
			end
			else begin
			   prbs_check_en             <=   LOW;
			end
		end
		else begin
		   prbs_check_en                <=   LOW;
		end
	end
end

//log frame_loss_crc error on Rx detection for a whole frame, using combiantional logic
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   frame_loss_crc_A2H                        <=   LOW;
	end
	else begin
	   if(frame_comma_mismatch) begin
		   frame_loss_crc_A2H                     <=   HIGH;
		end
		else if (frame_crc_err) begin
		   frame_loss_crc_A2H                     <=   HIGH;
		end
		else if (rx_frm_offset == 4'd11) begin
		   frame_loss_crc_A2H                     <=   LOW;
		end
	end
end

//////////////////////////Error injection handling///////////////////////////////
//Inject frame comma error
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_comma_err_d                   <=   LOW;
	end
	else begin
	   inject_frame_comma_err_d                   <=   inject_frame_comma_err_ind;
	end
end

always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_comma_err_flag                <=   LOW;
	end
	else begin
	   if(!inject_frame_comma_err_d && inject_frame_comma_err_ind) begin
		   inject_frame_comma_err_flag             <=   HIGH;
		end
		else if (inject_1_frame_comma_err && (inject_frame_comma_err_cnt == 1)) begin
		   inject_frame_comma_err_flag             <=   LOW;
		end
		else if (inject_3_frame_comma_err && (inject_frame_comma_err_cnt == 3)) begin
		   inject_frame_comma_err_flag             <=   LOW;
		end
	end
end

always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_comma_err_cnt                 <=   4'b0;
	end
	else begin
	   if(inject_frame_comma_err_flag) begin
		   if(tx_frm_offset == 12'b0)
		   inject_frame_comma_err_cnt              <=   inject_frame_comma_err_cnt + 1'b1;
		end
		else begin
		   inject_frame_comma_err_cnt              <=   4'b0;
		end
	end
end

//Inject frame crc error
always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_crc_err_d                   <=   LOW;
	end
	else begin
	   inject_frame_crc_err_d                   <=   inject_frame_crc_err_ind;
	end
end

always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_crc_err_flag                <=   LOW;
	end
	else begin
	   if(!inject_frame_crc_err_d && inject_frame_crc_err_ind) begin
		   inject_frame_crc_err_flag             <=   HIGH;
		end
		else if (inject_1_frame_crc_err && (inject_frame_crc_err_cnt == 1)) begin
		   inject_frame_crc_err_flag             <=   LOW;
		end
		else if (inject_10_frame_crc_err && (inject_frame_crc_err_cnt == 10)) begin
		   inject_frame_crc_err_flag             <=   LOW;
		end
	end
end

always @ (posedge clock or negedge reset_n) begin
   if(!reset_n) begin
	   inject_frame_crc_err_cnt                 <=   4'b0;
	end
	else begin
	   if(inject_frame_crc_err_flag) begin
		   if(tx_frm_offset == 4'd11)
		   inject_frame_crc_err_cnt              <=   inject_frame_crc_err_cnt + 1'b1;
		end
		else begin
		   inject_frame_crc_err_cnt              <=   4'b0;
		end
	end
end



//////////////////////////////////////////////////////////////////////////////////
// Instantiation
//////////////////////////////////////////////////////////////////////////////////	

//10ms timer
delay #(.COUNT(T_10mS_20M)) 
Timer10ms(
            .iClk                   ( clock                   ),
            .iRst                   ( reset_n                 ),
            .iStart                 ( timer_10ms_start        ),
            .iClrCnt                ( 1'b0                    ),
            .oDone                  ( timer_10ms_done         )
            );


//training comma lock state machine				
train_comma_lock_sm
#(
            .CONSECUTIVE_K28_5_LOCK ( CONSECUTIVE_K28_5_LOCK  )
)
train_comma_lock_sm_host
(
            .clock                  ( clock                   ),
            .reset_n                ( reset_n                 ),

            .lvds_rx_data           ( lvds_rx_data            ),
            .train_comma_sm_en      ( train_comma_sm_en       ),

            .bitslip                ( bit_slip                ),
            .train_comma_locked     ( train_comma_locked      )

);


//frame comma lock state machine
frame_comma_lock_sm
#(
            .CONSECUTIVE_K28_1_LOCK ( CONSECUTIVE_K28_1_LOCK  )
)
frame_comma_lock_sm_host
(
            .clock                  ( clock                   ),
            .reset_n                ( reset_n                 ),

            .lvds_rx_data           ( lvds_rx_data_d          ),
            .frame_comma_sm_en      ( frame_comma_sm_en       ),
				.rx_frm_offset          ( rx_frm_offset           ),

            .frame_comma_locked     ( frame_comma_locked      )

);


//frame loss state machine
frame_loss_sm
#(
            .CONSECUTIVE_K28_1_Loss ( CONSECUTIVE_K28_1_Loss  ),            
            .CONSECUTIVE_CRC_Loss   ( CONSECUTIVE_CRC_Loss    )                          
)
frame_loss_sm_host
(
            .clock                  ( clock                   ),
            .reset_n                ( reset_n                 ),

            .lvds_rx_data           ( lvds_rx_data_d          ),
            .frame_crc_err          ( frame_crc_err           ),
            .frame_loss_sm_en       ( frame_loss_sm_en        ),
            .frame_comma_mismatch   ( frame_comma_mismatch    ),
				.rx_frm_offset          ( rx_frm_offset           ),

            .frame_re_aligned       ( frame_re_aligned        ),
            .frame_lost             ( frame_lost              )

);

encoder_8b10b 
    #(
      .METHOD             (   0    )
      )
encoder_8b10b (
    .clk           ( clock                ),
    .rst           ( !reset_n             ),
    .kin_ena       ( enc_kin_ena          ),
    .ein_ena       ( enc_ein_ena          ),
    .ein_dat       ( frame_tx_data        ),
    .ein_rd        ( enc_rd               ),
    .eout_val      (                      ),
    .eout_dat      ( lvds_tx_data_postenc ),
    .eout_rdcomb   (                      ),
    .eout_rdreg    ( enc_rd               )
);

decoder_8b10b 
    #(
      .METHOD             (   0    ),
      .RDERR              (   0    ),
      .KERR               (   0    )
      )
decoder_8b10b (
    .clk           ( clock                ),
    .rst           ( !reset_n             ),
    .din_ena       ( 1'b1                 ),
    .din_dat       ( lvds_rx_data_d       ),
    .din_rd        ( dec_rd               ),
    .dout_val      (                      ),
    .dout_dat      ( frame_rx_data        ),
    .dout_k        (                      ),
    .dout_kerr     (                      ),
    .dout_rderr    (                      ),
    .dout_rdcomb   (                      ),
    .dout_rdreg    ( dec_rd               )
);


crc8 crc8_gen (
    .iClk  ( clock                 ),                
    .iRst  ( ~reset_n              ),
    .iClr  ( tx_frm_offset == 4'd0 ),   //Clear    CRC-8
    .iEn   ( crc_gen_en            ),   //Clock    enable
    .ivByte( frame_tx_data         ),   //Inbound  byte
    .ovCrc8( crc_data              )    //Outbound CRC-8 byte
);

crc8 crc8_check (
    .iClk  ( clock                    ),                 
    .iRst  ( ~reset_n                 ),
    .iClr  ( crc_check_clr            ),   //Clear    CRC-8
    .iEn   ( crc_check_en             ),   //Clock    enable
    .ivByte( frame_rx_data            ),   //Inbound  byte
    .ovCrc8( crc_check_result         )    //Outbound CRC-8 byte
);

lfsr_prbs_gen #(
    .LFSR_WIDTH    (31),
    .LFSR_POLY     (31'h10000001),
    .LFSR_INIT     (31'h7FFFFFFF),
    .LFSR_CONFIG   ("FIBONACCI"),
    .REVERSE       (0),
    .INVERT        (1),
    .DATA_WIDTH    (8),
    .STYLE         ("AUTO")
)
lfsr_prbs_gen_inst (
    .clk           (clock),
    .rst           (~reset_n),
    .enable        (prbs_gen_en),
    .data_out      (prbs_gen_data)
);

lfsr_prbs_check #(
    .LFSR_WIDTH    (31),
    .LFSR_POLY     (31'h10000001),
    .LFSR_INIT     (31'h7FFFFFFF),
    .LFSR_CONFIG   ("FIBONACCI"),
    .REVERSE       (0),
    .INVERT        (1),
    .DATA_WIDTH    (8),
    .STYLE         ("AUTO")
)
lfsr_prbs_check_inst (
    .clk           (clock),
    .rst           (~reset_n),
    .data_in       (prbs_check_data),
    .data_in_valid (prbs_check_en),
    .data_out      (prbs_check_result)
);

endmodule
